package qy.jalgotrade.eventprof;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.collections4.MapUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import it.unimi.dsi.fastutil.doubles.DoubleArrayList;
import jep.Jep;
import jep.JepException;
import jep.SharedInterpreter;
import jep.python.PyObject;
import qy.jalgotrade.utils.CommonUtils;

/**
 * @author qy
 *
 */
public class Plotter {

	private static final Logger logger = LoggerFactory.getLogger(Plotter.class);

	private static final String PLOTTER_PY = "py/plotter.py";

	private static final String PLOTTER_PY_ENCODING = "UTF-8";

	private static Jep jepInterp;

	static {

		try {
			jepInterp = new SharedInterpreter();
			String plotterPy = IOUtils.toString(Plotter.class.getClassLoader().getResourceAsStream(PLOTTER_PY),
					PLOTTER_PY_ENCODING);
			jepInterp.exec(plotterPy);
		} catch (Exception e) {
			logger.error("Exception occured initializing Jep Interpreter: {}", ExceptionUtils.getStackTrace(e));
		}
	}

	/**
	 * 
	 * @param profilerResults profilerResults
	 * @throws JepException JepException
	 */
	public static void buildPlot(Results profilerResults) throws JepException {

		// Calculate each value.
		List<Integer> x = new ArrayList<>();
		List<Double> mean = new ArrayList<>();
		List<Double> std = new ArrayList<>();
		for (int t = -profilerResults.getLookBack(); t < profilerResults.getLookForward() + 1; t++) {
			x.add(t);
			DoubleArrayList values = profilerResults.getValues(t);
			mean.add(CommonUtils.mean(values));
			std.add(CommonUtils.std(values));
		}

		// Cleanup
		PyObject plt = jepInterp.getValue("plt", PyObject.class);
		// plt.clf()
		CommonUtils.pyInvoke(plt, "clf");
		// Plot a line with the mean cumulative returns.
		// plt.plot(x, mean, color='#0000FF')
		Object[] args = new Object[] { x, mean };
		// @formatter:off
		Map<String, Object> kwargs = MapUtils.putAll(new HashMap<>(), new Object[][] {
				{ "color", "#0000FF" }
			});
		// @formatter:on
		CommonUtils.pyInvoke(plt, "plot", args, kwargs);

		// Error bars starting on the first lookforward period.
		int lookBack = profilerResults.getLookBack();
		int firstLookForward = lookBack + 1;
		// @formatter:off
		kwargs = MapUtils.putAll(new HashMap<String, Object>(), new Object[][] {
				{ "x", x.subList(firstLookForward, x.size()) },
				{ "y", mean.subList(firstLookForward, mean.size()) },
				{ "yerr", std.subList(firstLookForward, std.size()) },
				{ "capsize", 3 },
				{ "ecolor", "#AAAAFF" },
				{ "alpha", 0.5 },
			});
		// @formatter:on
		// plt.errorbar(
		//     x=x[firstLookForward:], y=mean[firstLookForward:], yerr=std[firstLookForward:],
		//     capsize=3,
		//     ecolor='#AAAAFF', alpha=0.5
		// )
		CommonUtils.pyInvoke(plt, "errorbar", kwargs);

		// Horizontal line at the level of the first cumulative return.
		// plt.axhline(
		//     y=mean[lookBack],
		//     xmin=-1*profilerResults.getLookBack(), xmax=profilerResults.getLookForward(),
		//     color='#000000'
		// )
		// @formatter:off
		kwargs = MapUtils.putAll(new HashMap<>(), new Object[][] {
				{ "y", mean.get(lookBack) },
				{ "xmin", -profilerResults.getLookBack() },
				{ "xmax", profilerResults.getLookForward() },
				{ "color", "#000000" }
			});
		// @formatter:on
		CommonUtils.pyInvoke(plt, "axhline", kwargs);

		// plt.xlim(profilerResults.getLookBack()*-1-0.5, profilerResults.getLookForward()+0.5)
		// plt.xlabel('Time')
		// plt.ylabel('Cumulative returns')
		CommonUtils.pyInvoke(plt, "xlim", -profilerResults.getLookBack() - 0.5, profilerResults.getLookForward() + 0.5);
		CommonUtils.pyInvoke(plt, "xlabel", "Time");
		CommonUtils.pyInvoke(plt, "ylabel", "Cumulative returns");
	}

	/**
	 * Plots the result of the analysis.
	 * 
	 * @param profilerResults The result of the analysis
	 * @throws JepException JepException
	 */
	public static void plot(Results profilerResults) throws JepException {

		buildPlot(profilerResults);
		// plt.show()
		CommonUtils.pyInvoke(jepInterp.getValue("plt", PyObject.class), "show");
	}
}
